## set chunk options
# knitr::opts_chunk$set(echo = FALSE)
# knitr::opts_chunk$set(include = TRUE)
# knitr::opts_chunk$set(warning = FALSE)
# knitr::opts_chunk$set(fig.width=7, fig.asp =0.618) 
# knitr::opts_chunk$set(comment = "#") # (NA, to remove all hashes)

## load libraries
library(tidyverse)
## -- Attaching packages ------------------------------------------------------------------------------------------ tidyverse 1.2.1 --
## v ggplot2 3.1.0     v purrr   0.2.5
## v tibble  1.4.2     v dplyr   0.7.8
## v tidyr   0.8.2     v stringr 1.3.1
## v readr   1.3.1     v forcats 0.3.0
## -- Conflicts --------------------------------------------------------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(ez)


# set up the theme for plot and rainclound plot
plot_theme = theme(
  text = element_text(size = 10),
  axis.title = element_text(size = 16), 
  axis.text = element_text(size = 14), # the size of the texts in plot
  # axis.text.x = element_text(angle = 45, vjust = 0.5),
  legend.title=element_text(size=16),
  legend.text=element_text(size=16),
  legend.position = "right",
  legend.key.width = unit(1.2, "cm"),
  plot.title = element_text(lineheight=.8, face="bold", size = 17),
  panel.border = element_blank(),
  panel.grid.minor = element_blank(),
  panel.grid.major = element_blank(),
  axis.line.x = element_line(colour = 'black', size=0.5, linetype='solid'),
  axis.line.y = element_line(colour = 'black', size=0.5, linetype='solid', arrow = arrow(length = unit(0.3, "cm"))),
  # remove the facet background color
  strip.text = element_text(face="bold", size=12, lineheight=5.0),
  strip.background = element_rect(fill="white", colour="white", size=1),
  strip.placement = "outside"
)

source("geom_flat_violin.R")

This is a R markdown file to analyze the data for the composite face task (complete design) without catch trials. (Experiment number is 104_6)

###### Optional ######
## the filename of the raw data file 
raw.file <- "104_106_n-20_2017-05-04-1446.csv"
if (raw.file == "") {
  raw.file <- file.choose()
}

## input the filename of the output file (.csv)
output.name <- ""

The raw data was loaded from the file named 104_106_n-20_2017-05-04-1446.csv.

1 Tidying up the data

# do you want save the data into *.csv files
# saveCSV = TRUE
saveCSV = FALSE

# read data file
raw.data <- read.csv(raw.file)
raw.filename <- basename(raw.file)
raw.dir <- dirname(raw.file)

# get the filename of the output
if (output.name == "") {
  output.name <- strsplit(raw.filename, ".csv")
}

# set the filenames for R and SPSS data format
output.d.R <- paste(raw.dir, "/", output.name, "_d_R.csv", sep = "")
output.d.SPSS <- paste(raw.dir, "/", output.name, "_d_SPSS.csv", sep = "")
output.rt.R <- paste(raw.dir, "/", output.name, "_rt_R.csv", sep = "")
output.rt.SPSS <- paste(raw.dir, "/", output.name, "_rt_SPSS.csv", sep = "")
# expLevel.old <- unique(rawData$Experiment)  # "104_CF_CFS" "106_CF_Mono"
# expLevel.new <- (c("CFS", "monocular"))


# clean and rename the levels of independent variables
raw.clean <- {
  raw.data %>%
    tbl_df() %>%  # change into table data frame format
    mutate(
      Participant = as.factor(Participant),
      Congruency = recode(Congruency, "C" = "congruent", "I" = "incongruent"), # rename the levels for Congruency
      Alignment = recode(Alignment, "A" = "aligned", "M" = "misaligned"),
      Experiment = recode(Experiment, "104_CF_CFS" = "CFS", "106_CF_Mono" = "monocular")
      ) %>% # rename the levels for Alignment
    group_by(Experiment, Participant, Congruency, Alignment, SameDifferent, isCorrect) %>% # divide into groups 
    mutate(
      RT.Z = scale(reactionTime), # calculate the Z value for response times within each group
      RT.Within3Z = ifelse(RT.Z <= 3 & RT.Z >= -3, 1, ifelse(RT.Z < -3 | RT.Z > 3, 0, NaN))
      ) %>%  # if the Z values are between -3 and 3, will be marked as 1. 
    ungroup()  # ungroup the data
}

raw.clean

2 Sensitivity d’

# calculate the d'
individual.d.R <- { # get the hit and correct rejection rates
  raw.clean %>%
    group_by(Experiment, Participant, Congruency, Alignment, SameDifferent) %>%  # divide the data into serveral groups 
    summarize(Accuracy = mean(isCorrect), Count = n()) %>% # get accuracy and count for each condition (hit and correct rejection)  
    mutate(Accuracy = ifelse(Accuracy == 1, 1 - 0.5/Count, Accuracy)) %>%  # set 1 equals to 1-0.5/n
    spread(SameDifferent, Accuracy) %>%  # spread the Accuracy into two columns based on SameDifferent
    mutate(FA = 1 - D,
           Z.hit = qnorm(S),
           Z.FA = qnorm(FA),
           dprime = Z.hit - Z.FA) %>% # get the d prime
    select(Experiment, Participant, Congruency, Alignment, dprime, Count)
}

# descriptive statistics of d for plotting
sum.d.R <- {
  individual.d.R %>% 
    group_by(Experiment, Congruency, Alignment) %>% 
    summarize(Mean = mean(dprime), N = n(), SD = sd(dprime), SE = SD/sqrt(N), SE.hi = Mean + SE, SE.lo = Mean - SE, CI = SE * qt(0.975,N), Median = median(dprime), Lower = Mean - SD, Upper = Mean + SD) %>% 
    ungroup()
}

# d for SPSS sytle analysis
individual.d.SPSS <- {
  individual.d.R %>%
    ungroup() %>%  # ungroup the data.d.r 
    mutate(Conditions = paste(Experiment, Congruency, Alignment, sep = ".")) %>% # combine all the levels together
    select(Participant, dprime, Conditions, Count) %>% # delete the Independet Variables
    spread(Conditions, dprime)
}

# save the d' for R analysis
if (saveCSV) {
  write.csv(individual.d.R, output.d.R, row.names = FALSE)
  write.csv(individual.d.SPSS, output.d.SPSS, row.names = FALSE)
}

2.1 RainClound plot of sensitivity (d’)

knitr::kable(sum.d.R, digits = 4)
Experiment Congruency Alignment Mean N SD SE SE.hi SE.lo CI Median Lower Upper
CFS congruent aligned 2.5001 20 0.6983 0.1561 2.6563 2.3440 0.3257 2.3970 1.8019 3.1984
CFS congruent misaligned 2.5646 20 0.5949 0.1330 2.6976 2.4316 0.2775 2.5899 1.9697 3.1596
CFS incongruent aligned 2.4831 20 0.7803 0.1745 2.6576 2.3086 0.3640 2.4250 1.7028 3.2634
CFS incongruent misaligned 2.5088 20 0.6606 0.1477 2.6565 2.3611 0.3081 2.4161 1.8483 3.1694
monocular congruent aligned 2.7825 20 0.5514 0.1233 2.9058 2.6592 0.2572 2.8591 2.2311 3.3339
monocular congruent misaligned 2.6769 20 0.7150 0.1599 2.8368 2.5170 0.3335 2.8204 1.9619 3.3920
monocular incongruent aligned 2.1700 20 0.8822 0.1973 2.3673 1.9727 0.4115 1.9600 1.2878 3.0522
monocular incongruent misaligned 2.4848 20 0.6284 0.1405 2.6253 2.3442 0.2931 2.4381 1.8563 3.1132
d.RainPlot <- {
  ggplot(data = individual.d.R, aes(y = dprime, x = Alignment, fill = Congruency)) + 
    geom_flat_violin(position = position_nudge(x = .2, y = 0), alpha = .7) +
    geom_point(aes(y = dprime, color = Congruency), position = position_jitter(width = .15), size = .5, alpha = .8) +
    geom_boxplot(aes(y = dprime), width = .2, outlier.shape = NA, alpha = .7) +
    geom_point(data = sum.d.R, aes(y = Mean, x = Alignment, color = Congruency), position = position_nudge(x = 0.3), size = 2.5) +
    geom_errorbar(data = sum.d.R, aes(y = Mean, ymin = Lower, ymax = Upper), position = position_nudge(x = 0.3), width = 0) +
    facet_grid(. ~ Experiment, switch = "both") +
    # scale_colour_grey() + # start = .1, end = .6, color for the contour
    # scale_fill_grey() + # start = .3, end = .6, color for the fill
    # scale_color_brewer(palette = "Set1") +
    # scale_fill_brewer(palette = "Set1") +
    labs(title = "Sensitivity d' for E104_106", x = "Experiment", y = "Sensitivity", fill = "Congruency", color = "Congruency") +  # set the names for main, x and y axises and the legend
    # coord_cartesian(ylim = c(0, 6)) +  # set the limit for y axis
    # scale_y_continuous(expand= c(0, 0)) +  # remove the space between columns and x axis
    # geom_hline(yintercept = 0, linetype = 5, alpha = 0.5) + # add the line for 0.5 and 1 (y)
    theme_bw() + # the backgroud color
    plot_theme
}
## Warning: Ignoring unknown aesthetics: y

 

d.RainPlot

2.2 Repeated measure ANOVA for sensitivity (d’)

2.2.1 Sensitivity d’: Experiment(2) \(\times\) Congruency(2) \(\times\) Alignment(2)

d.anova <- { 
  ezANOVA(data = individual.d.R
          , dv = dprime
          , wid = Participant
          , within = c(Experiment, Congruency, Alignment)
          , detailed = TRUE
          # , type = 3 # for this test, results for type II and III are the same
          # , return_aov = TRUE
  )
}

knitr::kable(d.anova$ANOVA, digits = 4)
Effect DFn DFd SSn SSd F p p<.05 ges
(Intercept) 1 19 1017.1601 49.3323 391.7520 0.0000 * 0.9325
Experiment 1 19 0.0083 6.2512 0.0251 0.8757 0.0001
Congruency 1 19 1.9252 4.5127 8.1056 0.0103 * 0.0255
Alignment 1 19 0.2241 3.3359 1.2762 0.2727 0.0030
Experiment:Congruency 1 19 1.3391 2.2886 11.1175 0.0035 * 0.0179
Experiment:Alignment 1 19 0.0354 2.8230 0.2382 0.6311 0.0005
Congruency:Alignment 1 19 0.3641 3.2080 2.1566 0.1583 0.0049
Experiment:Congruency:Alignment 1 19 0.5269 1.8787 5.3283 0.0324 * 0.0071

2.2.2 Sensitivity d’ for CFS: Congruency(2) \(\times\) Alignment(2)

d.anova.CFS <- {
  individual.d.R %>%
    filter(Experiment == thisExp) %>% 
    ezANOVA(
      dv = dprime,
      wid = Participant,
      within = .(Congruency, Alignment),
      detailed = TRUE,
      return_aov = TRUE
    )  # run repeaed measures ANOVA 
}

knitr::kable(d.anova.CFS$ANOVA, digits = 4)
Effect DFn DFd SSn SSd F p p<.05 ges
(Intercept) 1 19 505.6839 25.5391 376.2077 0.0000 * 0.9338
Congruency 1 19 0.0265 3.5854 0.1405 0.7119 0.0007
Alignment 1 19 0.0407 4.5854 0.1686 0.6860 0.0011
Congruency:Alignment 1 19 0.0075 2.1384 0.0666 0.7992 0.0002

2.2.3 Sensitivity d’ for monocular: Congruency(2) \(\times\) Alignment(2)

d.anova.CFS <- {
  individual.d.R %>%
    filter(Experiment == thisExp) %>% 
    ezANOVA(
      dv = dprime,
      wid = Participant,
      within = .(Congruency, Alignment),
      detailed = TRUE,
      return_aov = TRUE
    )  # run repeaed measures ANOVA 
}

knitr::kable(d.anova.CFS$ANOVA, digits = 4)
Effect DFn DFd SSn SSd F p p<.05 ges
(Intercept) 1 19 511.4845 30.0445 323.4605 0.0000 * 0.9312
Congruency 1 19 3.2378 3.2158 19.1296 0.0003 * 0.0789
Alignment 1 19 0.2188 1.5735 2.6416 0.1206 0.0058
Congruency:Alignment 1 19 0.8835 2.9483 5.6935 0.0276 * 0.0228

2.3 Line plot of sensitivity (d’)

d.ColuPlot = {
  ggplot(data = sum.d.R, aes(y = Mean, x = Alignment, color = Congruency, group = Congruency)) +  # set the data, varialbes for x and y axises, and the variable for dividing data
    geom_point() +
    geom_line() +  # position = "dodge", alpha = .7
    geom_errorbar(mapping = aes(ymin = SE.lo, ymax = SE.hi), linetype = 1,  # set the error bar
                  show.legend = FALSE, width = 0.25, alpha = .5) + # ,                   position = position_dodge(width=0.9)
    facet_grid(. ~ Experiment, switch = "both") +
    coord_cartesian(ylim = c(0, 4.5)) +  # set the limit for y axis
    scale_y_continuous(expand= c(0, 0)) +  # remove the space between columns and x axis
    labs(title = "Sensitivity for E104_106", x = "Experiment", y = "Sensitivity", fill = "Congruency") +  # set the names for main, x and y axises
    scale_fill_grey() +  # set the color for columns
    geom_text(label = c("", "", "", "", "***", "", "", ""), color = "red", size = 6, nudge_y = 0.5, nudge_x = 0.5) + # add starts to the significant columns
    theme_bw() +
    plot_theme
}

d.ColuPlot

3 correct Response Times

individual.rt.R <- {
  raw.clean %>%
    filter(RT.Within3Z == 1)  %>%  # only keep the RT within 3 Z
    filter(isCorrect == 1)  %>%  # only keep the correct correct
    group_by(Experiment, Participant, Congruency, Alignment) %>%  # divide the data into serveral groups 
    summarize(RT = mean(reactionTime) * 1000 + 300, Count = n()) %>% # get RT and count for each condition
    ungroup()
}

# descriptive statistics of RT for plotting
sum.rt.R <- {
  individual.rt.R %>% 
    select(-Count) %>% 
    group_by(Experiment, Congruency, Alignment) %>% 
    summarize(Mean = mean(RT), N = n(), SD = sd(RT), SE = SD/sqrt(N), SE.hi = Mean + SE, SE.lo = Mean - SE, CI = SE * qt(0.975,N), Median = median(RT), Lower = Mean - SD, Upper = Mean + SD) %>% 
    ungroup()
}

# RT for SPSS sytle analysis
individual.rt.SPSS <- {
  individual.rt.R %>%
    mutate(Conditions = paste(Experiment, Congruency, Alignment, sep = ".")) %>%
    select(Participant, Conditions, RT) %>% 
    spread(Conditions, RT)
}

# save the RT for R analysis
if (saveCSV) {
  write.csv(individual.rt.R, output.rt.R, row.names = FALSE)
  write.csv(individual.rt.SPSS, output.rt.SPSS, row.names = FALSE)
}

3.1 RainClound plot of correct response times

knitr::kable(sum.rt.R, digits = 4)
Experiment Congruency Alignment Mean N SD SE SE.hi SE.lo CI Median Lower Upper
CFS congruent aligned 542.2552 20 124.4380 27.8252 570.0804 514.4300 58.0423 525.4149 417.8172 666.6932
CFS congruent misaligned 548.6668 20 124.5759 27.8560 576.5228 520.8107 58.1067 525.4203 424.0908 673.2427
CFS incongruent aligned 548.0863 20 137.0902 30.6543 578.7406 517.4320 63.9438 537.7043 410.9960 685.1765
CFS incongruent misaligned 537.6101 20 117.2179 26.2107 563.8208 511.3994 54.6746 512.8179 420.3922 654.8280
monocular congruent aligned 562.0456 20 115.8470 25.9042 587.9498 536.1414 54.0352 560.2480 446.1986 677.8926
monocular congruent misaligned 563.0177 20 122.9075 27.4829 590.5006 535.5347 57.3284 567.5096 440.1102 685.9251
monocular incongruent aligned 578.6119 20 118.7580 26.5551 605.1669 552.0568 55.3929 587.7466 459.8539 697.3698
monocular incongruent misaligned 565.0116 20 122.5508 27.4032 592.4148 537.6084 57.1620 574.7539 442.4608 687.5624
rt.RainPlot <- {
  ggplot(data = individual.rt.R, aes(y = RT, x = Alignment, fill = Congruency)) + 
    geom_flat_violin(position = position_nudge(x = .2, y = 0), alpha = .7) +
    geom_point(aes(y = RT, color = Congruency), position = position_jitter(width = .15), size = .5, alpha = .8) +
    geom_boxplot(aes(y = RT), width = .2, outlier.shape = NA, alpha = .7) +
    geom_point(data = sum.rt.R, aes(y = Mean, x = Alignment, color = Congruency), position = position_nudge(x = 0.3), size = 2.5) +
    geom_errorbar(data = sum.rt.R, aes(y = Mean, ymin = Lower, ymax = Upper), position = position_nudge(x = 0.3), width = 0) +
    facet_grid(. ~ Experiment, switch = "both") +
    # scale_colour_grey() + # start = .1, end = .6, color for the contour
    # scale_fill_grey() + # start = .3, end = .6, color for the fill
    # scale_color_brewer(palette = "Set1") +
    # scale_fill_brewer(palette = "Set1") +
    labs(title = "Correct Response Times for E104_106", x = "Alignment", y = "Response Times (ms)", fill = "Congruency", color = "Congruency") +  # set the names for main, x and y axises and the legend
    # coord_cartesian(ylim = c(0, 6)) +  # set the limit for y axis
    # scale_y_continuous(expand = c(0, 0)) +  # remove the space between columns and x axis
    # geom_hline(yintercept = 0, linetype = 5, alpha = 0.5) + # add the line for 0.5 and 1 (y)
    theme_bw() + # the backgroud color
    plot_theme
}
## Warning: Ignoring unknown aesthetics: y

 

rt.RainPlot

3.2 Repeated measure ANOVA for response times

3.2.1 Correct RT: Experiment(2) \(\times\) Congruency(2) \(\times\) Alignment(2)

rt.anova <- { 
  ezANOVA(data = individual.rt.R
          , dv = RT
          , wid = Participant
          , within = c(Experiment, Congruency, Alignment)
          , detailed = TRUE
          # , type = 3 # for this test, results for type II and III are the same
          # , return_aov = TRUE
  )
}

knitr::kable(rt.anova$ANOVA, digits = 4)
Effect DFn DFd SSn SSd F p p<.05 ges
(Intercept) 1 19 4.940184e+07 2097279.139 447.5489 0.0000 * 0.9555
Experiment 1 19 2.119146e+04 150925.609 2.6678 0.1189 0.0091
Congruency 1 19 4.445290e+02 3146.660 2.6841 0.1178 0.0002
Alignment 1 19 6.966238e+02 10381.039 1.2750 0.2729 0.0003
Experiment:Congruency 1 19 1.414405e+03 6115.301 4.3945 0.0497 * 0.0006
Experiment:Alignment 1 19 1.833385e+02 17006.359 0.2048 0.6560 0.0001
Congruency:Alignment 1 19 2.474343e+03 9486.172 4.9559 0.0383 * 0.0011
Experiment:Congruency:Alignment 1 19 1.340280e+01 8206.872 0.0310 0.8620 0.0000

3.2.2 Correct RT for CFS: Congruency(2) \(\times\) Alignment(2)

rt.anova.CFS <- {
  individual.rt.R %>%
    filter(Experiment == thisExp) %>%
    ezANOVA(
      dv = RT,
      wid = Participant,
      within = .(Congruency, Alignment),
      detailed = TRUE,
      return_aov = TRUE
    )  # run repeaed measures ANOVA 
}

knitr::kable(rt.anova.CFS$ANOVA, digits = 4)
Effect DFn DFd SSn SSd F p p<.05 ges
(Intercept) 1 19 2.368834e+07 1188680.813 378.6369 0.0000 * 0.9515
Congruency 1 19 1.365332e+02 4449.812 0.5830 0.4545 0.0001
Alignment 1 19 8.260470e+01 8670.159 0.1810 0.6753 0.0001
Congruency:Alignment 1 19 1.425980e+03 5416.759 5.0018 0.0375 * 0.0012

3.2.3 Correct RT for monocular: Congruency(2) \(\times\) Alignment(2)

rt.anova.CFS <- {
  individual.rt.R %>%
    filter(Experiment == thisExp) %>%
    ezANOVA(
      dv = RT,
      wid = Participant,
      within = .(Congruency, Alignment),
      detailed = TRUE,
      return_aov = TRUE
    )  # run repeaed measures ANOVA 
}

knitr::kable(rt.anova.CFS$ANOVA, digits = 4)
Effect DFn DFd SSn SSd F p p<.05 ges
(Intercept) 1 19 2.573470e+07 1059523.94 461.4896 0.0000 * 0.9592
Congruency 1 19 1.722401e+03 4812.15 6.8006 0.0173 * 0.0016
Alignment 1 19 7.973576e+02 18717.24 0.8094 0.3796 0.0007
Congruency:Alignment 1 19 1.061765e+03 12276.29 1.6433 0.2153 0.0010

3.3 Line plot of correct response times

rt.ColuPlot = {
  ggplot(data = sum.rt.R, aes(y = Mean, x = Alignment, color = Congruency, group = Congruency)) +  # set the data, varialbes for x and y axises, and the variable for dividing data
    geom_point() +
    geom_line() +  # position = "dodge", alpha = .7
    geom_errorbar(mapping = aes(ymin = SE.lo, ymax = SE.hi), linetype = 1,  # set the error bar
                  show.legend = FALSE, width = 0.25, alpha = .5) + # ,                   position = position_dodge(width=0.9)
    coord_cartesian(ylim = c(0, 1100)) +  # set the limit for y axis
    scale_y_continuous(expand= c(0, 0)) +  # remove the space between columns and x axis
    facet_grid(. ~ Experiment, switch = "both") +
    labs(title = "Correct Response Times for E104_106", x = "Experiment", y = "Response Times (ms)", fill = "Congruency") +  # set the names for main, x and y axises
    scale_fill_grey() +  # set the color for columns
    geom_text(label = c("*", "", "", "", "", "", "", ""), color = "red", size = 6, nudge_y = 30, nudge_x = 0.5) + # add starts to the significant columns
    theme_bw() +
    plot_theme
}

rt.ColuPlot

4 Logit Mixed Model

Create dummy coding:

raw.clean <- {
  raw.clean %>%
    mutate(
      Experiment_D = ifelse(Experiment == "CFS", 0, ifelse(Experiment == "monocular", 1, NaN)),
      Congruency_D = ifelse(Congruency == "congruent", 0, ifelse(Experiment == "incongruent", 1, NaN)),
      Alignment_D = ifelse(Alignment == "aligned", 0, ifelse(Alignment == "misaligned", 1, NaN)),
      Experiment_Congruency = Experiment_D * Congruency_D,
      Experiment_Alignment = Experiment_D * Alignment_D,
      Congruency_Alignment = Congruency_D * Alignment_D,
      Experiment_Congruency_Alignment = Experiment_Congruency * Alignment_D
    )
  
}

write_csv(raw.clean, "104_106_contrast.csv")
 

A work by Haiyang Jin